///<reference path="./Operators.ts"/>
namespace mecs {
    export class OpFunc {
        private static _inst: OpFunc;
        static get inst() { return this._inst || new OpFunc() }
        private _hash: any;
        constructor() {
            this._hash = {};
            this._registFunc(Op.gt, this._gtFunc);
            this._registFunc(Op.gte, this._gteFunc);
            this._registFunc(Op.between, this._betweenFunc);
            this._registFunc(Op.lt, this._ltFunc);
            this._registFunc(Op.lte, this._lteFunc);
            this._registFunc(Op.ne, this._neFunc);
            this._registFunc(Op.notBetween, this._notBetweenFunc);
        }

        private _registFunc(funcName: symbol, func: (value: WhereValue, currValue: WhereValue) => boolean) {
            this._hash[funcName] = func;
        }

        check<T>(funcName: symbol, value: WhereValue, currValue: WhereValue): boolean {
            let func: Function = this._hash[funcName];
            if (func) {
                return func.call(this, value, currValue);
            }
            return true;
        }

        private _gtFunc(value: WhereValue, currValue: WhereValue) {
            return value > currValue;
        }

        private _gteFunc(value: WhereValue, currValue: WhereValue) {
            return value >= currValue;
        }

        private _betweenFunc(value: WhereValue, currValue: WhereValue) {
            return value >= currValue[0] && value <= currValue[1];
        }

        private _ltFunc(value: WhereValue, currValue: WhereValue) {
            return value < currValue;
        }

        private _lteFunc(value: WhereValue, currValue: WhereValue) {
            return value <= currValue;
        }

        private _neFunc(value: WhereValue, currValue: WhereValue) {
            return value != currValue;
        }

        private _notBetweenFunc(value: WhereValue, currValue: WhereValue) {
            return value <= currValue[0] || value >= currValue[1];
        }
    }

    export interface WhereOperators {
        [Op.gt]?: number | string;
        [Op.gte]?: number | string;
        [Op.lt]?: number | string;
        [Op.lte]?: number | string;
        [Op.between]?: Array<number | string>;
        [Op.notBetween]?: Array<number | string>;
        [Op.ne]?: number | string;
    }
}